﻿#region Header

/*
Behavioral Rating of Dancing Human Crowds based on Motion Patterns
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using Emgu.CV;
using Emgu.CV.Structure;
using paravili.Services;
using Sebarf.Services.Interfaces;

#endregion

namespace paravili.Steps {
	/// <summary>
	/// crop dead region of the given image. the cropped region has to be enter manually
	/// </summary>
	public class ManuallyCropDeadRegions : ProcessStepWithMeasurement<Image<Lab, byte>>, INotifyPropertyChanged {
		#region Public Properties

		[ConfigurabelStringValue(Name = "list of polygons that has to crop")]
		public string PolygonLists {
			get { return m_polygonLists; }
			set {
				m_polygonLists = value;
				EvaluatePointsLists(value);
				if (PropertyChanged != null) {
					PropertyChanged(this, new PropertyChangedEventArgs("PointsLists"));
				}
			}
		}

		[ConfigurableLabValue(Name = "sets the colour of the filtered region")]
		public Lab FilteredRegionColour { get; set; }

		[ConfigurabelBooleanValue(Name = "is step enable")]
		public bool IsEnable { get; set; }

		[ConfigurableValue()]
		public ObservableCollection<RegionFilterDataByCaptureSource> DeadRegionsByCaptureSource { get; set; }

		[ServiceRequest]
		public ICurrentCaptureSourceInformationLocatorService CurrentCaptureSourceInformationLocatorService {
			get { return m_currentCaptureSourceInformationLocatorService; }
			set {
				m_currentCaptureSourceInformationLocatorService = value;
				if (value != null) {
					m_currentCaptureSourceInformationLocatorService.CurrentCaputreSourceChanged += OnCapureSourceChanged;
				}
			}
		}

		public string ListSeperator { get; set; }
		public string ValueSeperator { get; set; }
		public string PointSeperator { get; set; }

		private void OnCapureSourceChanged(object sender, CaptureSourceInformationEventArgs e) {
			SavePoints(e.OldCaptureSource);
			LoadPoints(e.NewCaptureSource);
		}

		#endregion

		#region Public Methods

		public ManuallyCropDeadRegions() {
			ListSeperator = @"\";
			ValueSeperator = ",";
			PointSeperator = ";";
			m_listOfPoints = new List<Point[]>();
			FilteredRegionColour = new Lab(20, 20, 20);
			IsEnable = true;
			DeadRegionsByCaptureSource = new ObservableCollection<RegionFilterDataByCaptureSource>();
		}

		#endregion

		#region Private Methods

		private void LoadPoints(ICaptureSource iCaptureSource) {
			if (iCaptureSource != null) {
				RegionFilterDataByCaptureSource[] data =
					DeadRegionsByCaptureSource.Where(x => x.CaptureSourceName == iCaptureSource.Name).ToArray();
				PolygonLists = data.Length > 0 ? data[0].PointsLists : string.Empty;
			}
		}

		private void SavePoints(ICaptureSource iCaptureSource) {
			if (iCaptureSource != null) {
				RegionFilterDataByCaptureSource[] data =
					DeadRegionsByCaptureSource.Where(x => x.CaptureSourceName == iCaptureSource.Name).ToArray();
				if (data.Length > 0) {
					data[0].PointsLists = PolygonLists;
				}
				else {
					DeadRegionsByCaptureSource.Add(new RegionFilterDataByCaptureSource { CaptureSourceName = iCaptureSource.Name, PointsLists = PolygonLists });
				}
			}
		}

		private void EvaluatePointsLists(string value) {
			m_listOfPoints.Clear();
			if (!string.IsNullOrEmpty(value)) {
				var converter = new StringToPointConvertor();
				foreach (string l in value.Split(new[] { ListSeperator }, StringSplitOptions.RemoveEmptyEntries)) {
					try {
						Point[] result = converter.ConvertFromString(l);
						if (result.Length > 0) {
							m_listOfPoints.Add(result);
						}
					}
					catch (FormatException) {
						m_listOfPoints.Clear();
						return;
					}
				}
			}


			m_SizeCalculated = false;
		}
		bool m_SizeCalculated = false;

		void DrawPolygonOnImage(Image<Lab, byte> img, List<Point[]> ListOfPolygons) {
			foreach (var p in ListOfPolygons) {
				img.FillConvexPoly(p, FilteredRegionColour);
			}
		}

		protected override Image<Lab, byte> OnProcess(Image<Lab, byte> toProcess) {
			if (toProcess == null || !IsEnable) {
				return toProcess;
			}

			if (!m_SizeCalculated) {
				Image<Lab, byte> calcImg = new Image<Lab, byte>(toProcess.Size);
				DrawPolygonOnImage(calcImg, m_listOfPoints);
				var data = calcImg.Data;
				int cnt = 0;
				for (int i = 0; i < calcImg.Width; i++)
					for (int j = 0; j < calcImg.Height; j++) {
						if (data[j, i, 0] != 0)
							cnt++;
					}
				m_SizeCalculated = true;
				StatisticValuesProviderService.SetStatisticValue("ImageSizeRealRegions", (calcImg.Width * calcImg.Height) - cnt);
			}

			DrawPolygonOnImage(toProcess, m_listOfPoints);

			return toProcess;
		}

		#endregion

		#region Private Fields

		private readonly List<Point[]> m_listOfPoints;
		private ICurrentCaptureSourceInformationLocatorService m_currentCaptureSourceInformationLocatorService;
		private string m_polygonLists;

		#endregion

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		#region Nested type: StringToPointConvertor

		public class StringToPointConvertor {
			public Point[] ConvertFromString(string s) {
				const string valueSeperator = ",";
				const string pointSeperator = ";";
				return ConvertFromString(s, valueSeperator, pointSeperator);
			}

			public Point[] ConvertFromString(string s, string valueSeperator, string pointSeperator) {
				if (string.IsNullOrEmpty(s)) {
					return new Point[] { };
				}
				s = s.Trim();
				var toReturn = new List<Point>();

				string[] points = s.Split(new[] { pointSeperator }, StringSplitOptions.RemoveEmptyEntries);

				foreach (string p in points) {
					string[] values = p.Split(new[] { valueSeperator }, StringSplitOptions.RemoveEmptyEntries);
					if (values.Length != 2) {
						throw new FormatException();
					}
					double xValue = double.Parse(values[0]);
					double yValue = double.Parse(values[1]);
					toReturn.Add(new Point((int)xValue, (int)yValue));
				}
				return toReturn.ToArray();
			}
		}

		#endregion
	}

	public class RegionFilterDataByCaptureSource {
		[ConfigurableValue()]
		public string PointsLists { get; set; }
		[ConfigurableValue()]
		public string CaptureSourceName { get; set; }
	}
}